package dao

import (
	"context"
	"errors"
	"fmt"
	"quant/backend/api/dto"
	"quant/backend/conf"
	"quant/backend/db"
	"reflect"
	"sync"

	"github.com/sirupsen/logrus"
	"xorm.io/xorm"
)

var (
	ErrRecordNotFound = errors.New("没有找到记录")
	ErrInvalidID      = errors.New("无效的ID")
	ErrNoRowsAffected = errors.New("没有受影响的行")
	ErrEmptyColumns   = errors.New("空的数据列集合")
)

// BaseDao 基础Dao泛型类
type BaseDao[T any] struct {
	engine    *xorm.Engine
	session   *xorm.Session
	tableName string
	mutex     sync.Mutex // 改为实例级锁
}

// NewBaseDao 创建BaseDao实例
func NewBaseDao[T any]() *BaseDao[T] {
	var model T
	tableName := getTableName(model, db.GetMySQL())
	return &BaseDao[T]{
		engine:    db.GetMySQL(),
		session:   nil,
		tableName: tableName,
		mutex:     sync.Mutex{},
	}
}

// 获取表名（支持自定义 TableName 方法）
func getTableName[T any](model T, engine *xorm.Engine) string {
	if tn, ok := any(model).(interface{ TableName() string }); ok {
		return tn.TableName()
	}
	return engine.TableName(model) // 使用 xorm 默认表名规则
}

// 基础CRUD操作
// --------------------------------------------------

// GetByID 根据ID获取记录
func (dao *BaseDao[T]) GetByID(ctx context.Context, id any) (*T, error) {
	if id == nil || reflect.ValueOf(id).IsZero() {
		return nil, ErrInvalidID
	}
	var result T
	has, err := dao.engine.ID(id).Get(&result)
	if err != nil {
		return nil, fmt.Errorf("BaseDao.GetByID failed: %w", err) // 添加错误上下文
	}
	if !has {
		return nil, ErrRecordNotFound
	}
	return &result, nil
}

// Create 创建记录
func (dao *BaseDao[T]) Create(ctx context.Context, entity *T) (int64, error) {
	dao.mutex.Lock()
	defer dao.mutex.Unlock()
	if dao.session == nil {
		dao.session = dao.engine.NewSession()
	}

	defer func() {
		if !dao.session.IsInTx() {
			dao.session.Close()
			dao.session = nil
		}
	}()
	return dao.session.Insert(entity)
}

// Update 更新记录
func (dao *BaseDao[T]) Update(ctx context.Context, cols []string, id any, entity *T) error {
	if id == nil || reflect.ValueOf(id).IsZero() {
		return ErrInvalidID
	}

	dao.mutex.Lock()
	defer dao.mutex.Unlock()
	if dao.session == nil {
		dao.session = dao.engine.NewSession()
	}

	defer func() {
		if !dao.session.IsInTx() {
			dao.session.Close()
			dao.session = nil
		}
	}()

	if len(cols) == 0 {
		return ErrEmptyColumns
	}

	session := dao.session.ID(id)
	if cols != nil {
		session = session.Cols(cols...)
	}

	affected, err := session.Update(entity)
	if err != nil {
		return fmt.Errorf("database update failed: %w", err)
	}
	if affected == 0 {
		return ErrNoRowsAffected
	}
	return nil
}

// Delete 删除记录
func (dao *BaseDao[T]) Delete(ctx context.Context, id any) error {
	dao.mutex.Lock()
	defer dao.mutex.Unlock()
	if dao.session == nil {
		dao.session = dao.engine.NewSession()
	}
	defer func() {
		if !dao.session.IsInTx() {
			dao.session.Close()
			dao.session = nil
		}
	}()
	affected, err := dao.session.ID(id).Delete(new(T))
	if err != nil {
		return err
	}
	if affected == 0 {
		return ErrRecordNotFound
	}
	return nil
}

func (dao *BaseDao[T]) Truncate(ctx context.Context) error {
	dao.mutex.Lock()
	defer dao.mutex.Unlock()
	if dao.session == nil {
		dao.session = dao.engine.NewSession()
	}
	defer func() {
		if !dao.session.IsInTx() {
			dao.session.Close()
			dao.session = nil
		}
	}()

	tableName := getTableName(new(T), dao.engine)
	_, err := dao.session.Exec("TRUNCATE TABLE " + tableName)
	if err != nil {
		return err
	}
	return nil
}

// FindWhere 查询所有记录（带分页）
func (dao *BaseDao[T]) FindWhere(ctx context.Context, query any, pageNo, pageSize int) ([]T, error) {
	var results []T
	dao.session = dao.engine.NewSession()
	defer func(session *xorm.Session) {
		err := session.Close()
		if err != nil {
			logrus.Errorf("事务回滚: %v", err)
		}
	}(dao.session)

	if query != nil {
		dao.session.Where(query)
	}
	if pageNo > 0 && pageSize > 0 {
		dao.session.Limit(pageSize, (pageNo-1)*pageSize)
	}
	err := dao.session.Find(&results)
	return results, err
}

// FindAll 查询所有记录（带分页）
func (dao *BaseDao[T]) FindAll(ctx context.Context, query any, pageNo, pageSize int) ([]T, error) {
	var results []T
	dao.session = dao.engine.NewSession()
	defer func(session *xorm.Session) {
		err := session.Close()
		if err != nil {
			logrus.Errorf("事务回滚: %v", err)
		}
	}(dao.session)

	if query != nil {
		dao.session.Where(query)
	}
	if pageNo > 0 && pageSize > 0 {
		dao.session.Limit(pageSize, (pageNo-1)*pageSize)
	}
	err := dao.session.Find(&results)
	return results, err
}

// WithTx 开启事务
func (dao *BaseDao[T]) WithTx(ctx context.Context, fn func(*BaseDao[T]) error) error {
	dao.session = dao.engine.NewSession()
	defer func(session *xorm.Session) {
		err := session.Close()
		if err != nil {
			logrus.Errorf("事务回滚: %v", err)
		}
	}(dao.session)

	if err := dao.session.Begin(); err != nil {
		return err
	}

	if err := fn(dao); err != nil {
		_ = dao.session.Rollback()
		logrus.Errorf("事务回滚: %v", err)
		return err
	}

	return dao.session.Commit()
}

// PagingQuery 通用分页查询方法
func (dao *BaseDao[T]) PagingQuery(ctx context.Context, pageNo, pageSize int, queryFn func(*xorm.Session)) (*dto.PageResultDto[T], error) {
	dao.session = dao.engine.NewSession()
	defer func(session *xorm.Session) {
		err := session.Close()
		if err != nil {
			logrus.Errorf("事务回滚: %v", err)
		}
	}(dao.session)

	logrus.Info(ctx.Value("username"))

	// pageResult, err := dao.initPage(int64(pageNo), int64(pageSize), queryFn)
	pageResult, err := dao.initPage(int64(pageNo), int64(pageSize), queryFn)
	if err != nil {
		return nil, err
	}

	// 执行分页查询
	dao.session.Limit(int(pageResult.PageSize), int((pageResult.PageNo-1)*pageResult.PageSize))
	if queryFn != nil {
		queryFn(dao.session)
	}

	// 声明一个空切片, 注意,如果 list 为空时, 转 json,会变成 null
	// var list []T
	var list = make([]T, 0) // 声明和实例化（创建）一个空的切片
	if err := dao.session.Find(&list); err != nil {
		return nil, err
	}
	pageResult.List = list
	return pageResult, nil
}

func (dao *BaseDao[T]) initPage(pageNo, pageSize int64, queryFn func(*xorm.Session)) (*dto.PageResultDto[T], error) {
	if pageNo < 1 {
		pageNo = conf.Config.App.PageNo
	}
	if pageSize < 1 {
		pageSize = conf.Config.App.PageSize
	}
	// 创建新会话避免污染
	countSession := dao.engine.NewSession()
	defer countSession.Close()
	// 执行自定义查询条件
	if queryFn != nil {
		queryFn(countSession)
	}
	// 获取总数
	total, err := countSession.Count(new(T))
	if err != nil {
		return nil, err
	}
	var pageCount int64 = 0
	if total%pageSize == 0 {
		pageCount = total / pageSize
	} else {
		pageCount = total/pageSize + 1
	}
	pageResult := &dto.PageResultDto[T]{
		PageNo:    pageNo,
		PageSize:  pageSize,
		PageCount: pageCount,
		Total:     total,
	}
	return pageResult, nil
}
